package owl2vowl.model.data;

import owl2vowl.model.data.VowlData;
import owl2vowl.model.entities.AbstractEntity;
import owl2vowl.model.entities.nodes.classes.VowlClass;
import owl2vowl.model.entities.nodes.classes.VowlThing;
import owl2vowl.model.entities.nodes.datatypes.VowlDatatype;
import owl2vowl.model.entities.nodes.datatypes.VowlLiteral;
import owl2vowl.model.entities.properties.TypeOfProperty;
import owl2vowl.model.entities.properties.VowlDatatypeProperty;
import owl2vowl.model.entities.properties.VowlObjectProperty;
import owl2vowl.model.individuals.VowlIndividual;
import owl2vowl.model.visitor.VowlElementVisitor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.semanticweb.owlapi.model.IRI;

public class VowlSearcher implements VowlElementVisitor {
	private final VowlData data;
	private int dataSize = 0;
	private Set<VowlClass> intersections = new HashSet();
	private Set<VowlClass> unions = new HashSet();
	private Set<VowlThing> things = new HashSet();
	private Map<IRI, Set<IRI>> disjointMapping = new HashMap();

	public VowlSearcher(VowlData data) {
		this.data = data;
		this.dataSize = data.getEntityMap().keySet().size();
	}

	public Set<VowlThing> getThings() {
		this.checkConsistenty();
		return Collections.unmodifiableSet(this.things);
	}

	protected void refresh() {
		this.intersections.clear();
		this.unions.clear();
		this.things.clear();
		this.disjointMapping.clear();
		Iterator arg0 = this.data.getEntityMap().values().iterator();

		while (arg0.hasNext()) {
			AbstractEntity abstractEntity = (AbstractEntity) arg0.next();
			abstractEntity.accept(this);
		}

	}

	protected void checkConsistenty() {
		int currentSize = this.data.getEntityMap().keySet().size();
		if (currentSize != this.dataSize) {
			this.refresh();
			this.dataSize = currentSize;
		}

	}

	public void visit(VowlThing vowlThing) {
		this.things.add(vowlThing);
	}

	public void visit(VowlClass vowlClass) {
		if (vowlClass.getElementOfIntersection().size() != 0) {
			this.intersections.add(vowlClass);
		}

		if (vowlClass.getElementsOfUnion().size() != 0) {
			this.unions.add(vowlClass);
		}

	}

	public void visit(VowlLiteral vowlLiteral) {
	}

	public void visit(VowlDatatype vowlDatatype) {
	}

	public void visit(VowlObjectProperty vowlObjectProperty) {
		if (vowlObjectProperty.getType().equals("owl:disjointWith")) {
			this.addDisjoint(new IRI[]{(IRI) vowlObjectProperty.getDomains().iterator().next(),
					(IRI) vowlObjectProperty.getRanges().iterator().next()});
		}

	}

	public void visit(VowlDatatypeProperty vowlDatatypeProperty) {
		if (vowlDatatypeProperty.getType().equals("owl:disjointWith")) {
			this.addDisjoint(new IRI[]{(IRI) vowlDatatypeProperty.getDomains().iterator().next(),
					(IRI) vowlDatatypeProperty.getRanges().iterator().next()});
		}

	}

	public void visit(VowlIndividual vowlIndividual) {
	}

	public void visit(TypeOfProperty typeOfProperty) {
	}

	public boolean containsDisjoint(IRI... disjoint) {
		this.checkConsistenty();
		return this.disjointMapping.containsKey(disjoint[0])
				&& ((Set) this.disjointMapping.get(disjoint[0])).contains(disjoint[1]);
	}

	public VowlClass getIntersection(Collection<IRI> intersectionIris) {
		this.checkConsistenty();
		Iterator arg1 = this.intersections.iterator();

		VowlClass intersection;
		do {
			if (!arg1.hasNext()) {
				return null;
			}

			intersection = (VowlClass) arg1.next();
		} while (!intersection.getElementOfIntersection().containsAll(intersectionIris));

		return intersection;
	}

	public VowlClass getUnion(Set<IRI> iriList) {
		this.checkConsistenty();
		Iterator arg1 = this.unions.iterator();

		VowlClass union;
		do {
			if (!arg1.hasNext()) {
				return null;
			}

			union = (VowlClass) arg1.next();
		} while (!union.getElementOfIntersection().containsAll(iriList));

		return union;
	}

	protected void addDisjoint(IRI... disjoints) {
		if (!this.disjointMapping.containsKey(disjoints[0])) {
			this.disjointMapping.put(disjoints[0], new HashSet());
		}

		if (!this.disjointMapping.containsKey(disjoints[1])) {
			this.disjointMapping.put(disjoints[1], new HashSet());
		}

		((Set) this.disjointMapping.get(disjoints[0])).add(disjoints[1]);
		((Set) this.disjointMapping.get(disjoints[1])).add(disjoints[0]);
	}
}